#ifndef SIMPLE_SUPPORT_TUPLE_UTILS_DIG_HPP
#define SIMPLE_SUPPORT_TUPLE_UTILS_DIG_HPP

#include <tuple> // std::get std::tuple
#include <type_traits> // std::remove_cvref_t std::true_type std::false_type std::conditional_t std::is_same_v
#include <cstddef> // std::size_t
#include <utility> // std::forward std::index_sequence std::pair

#include "../type_traits/has_tuple_interface.hpp" // has_tuple_interface_v
#include "../carcdr.hpp" // car cdr
#include "../meta/find.hpp" // meta::find_t
#include "../meta/bind.hpp" // meta::bind
#include "../meta/integer_sequence.hpp" // meta::reverse_sequence_t
#include "../meta/list.hpp" // meta::list

namespace simple::support
{
	template <std::size_t ... i>
	void eat_an_index_sequence_for_breakfast(std::index_sequence<i...>) {}

	template <typename I>
	concept IndexSequence = requires
	{
		// []<std::size_t ... i>(std::index_sequence<i...>){}(I{}); // and why this no work?
		eat_an_index_sequence_for_breakfast(I{});
	};

	template <typename T>
	concept tuple_like = has_tuple_interface_v<std::remove_cv_t<T>>;

	template <IndexSequence I, tuple_like Tuple>
	[[nodiscard]]
	constexpr decltype(auto) dig(Tuple&& t)
	// TODO noexcept
	{
		using std::get;
		if constexpr(I::size() == 1)
			return get<car<I>>(std::forward<Tuple>(t));
		else return dig<cdr<I>>(get<car<I>>(std::forward<Tuple>(t)));
	}

	template <std::size_t ... i, tuple_like Tuple>
	[[nodiscard]]
	constexpr decltype(auto) dig(Tuple&& t)
	// TODO noexcept
	{
		return dig<std::index_sequence<i...>>(std::forward<Tuple>(t));
	}

	template <typename List, tuple_like T, std::size_t end>
	struct to_meta_list;
	template <typename List, tuple_like T, std::size_t i>
	requires (0 == i)
	struct to_meta_list<List, T, i>
	{ using type = meta::append_t<List, std::tuple_element_t<i, T>>; };
	template <typename List, tuple_like T, std::size_t i>
	requires ( i < std::tuple_size_v<T> && i != 0 )
	struct to_meta_list<List, T, i>
	{ using type = meta::append_t<to_meta_list<List,T,i-1>, std::tuple_element_t<i, T>>; };
	template <typename List, tuple_like T>
	using to_meta_list_t = typename to_meta_list<List, T, 0>::type;

	template <typename N, typename index, typename T>
	struct deep_find_pair_tuple_operator;
	template <typename N, typename T, std::size_t j, std::size_t ... i>
	struct deep_find_pair_tuple_operator<N, std::index_sequence<j, i...>, T>
	{
		static constexpr bool value = std::is_same_v<std::remove_cv_t<T>, N>;
		using binding = std::conditional_t<value,
			std::index_sequence<j, i...>,
			std::index_sequence<j+1, i...>
		>;
	};
	template <typename N, tuple_like T, std::size_t j, std::size_t ... i>
	struct deep_find_pair_tuple_operator<N, std::index_sequence<j, i...>, T>
	{
		using subfound = meta::find_t<
			meta::bind<
				meta::bind<deep_find_pair_tuple_operator, N>::template bound,
				std::index_sequence<0, j, i...>
			>,
			to_meta_list_t<meta::list<>,std::remove_cvref_t<T>>
		>;
		static constexpr bool value = (subfound::value != std::tuple_size_v<std::remove_cvref_t<T>>);
		using binding = std::conditional_t<value,
			typename subfound::functor::binding,
			std::index_sequence<j+1, i...>
		>;
	};

	template <typename Element, tuple_like Tuple>
	[[nodiscard]]
	constexpr decltype(auto) dig(Tuple&& t)
	// TODO noexcept
	{
		using found = meta::find_t<
			meta::bind<
				meta::bind<deep_find_pair_tuple_operator, Element>::template bound,
				std::index_sequence<0>
			>,
			to_meta_list_t<meta::list<>, std::remove_cvref_t<Tuple>>
		>;
		return dig<meta::reverse_sequence_t<typename found::functor::binding>>(std::forward<Tuple>(t));
	}


} // namespace simple::support

#endif /* end of include guard */
